/*
 * spi.c
 *
 *  Created on: Aug 3, 2013
 *      Author: Ken Arok
 *
 * \file
 *
 * \brief Hardware Abstraction Layer of SPI Controller UC3C0512C
 *
 * Copyright (c) 2013 PT Hanindo Automation Solutions. All rights reserved.
 *
 */

#include "config_board.h"

#if BOARD_1_0_USED

#include <string.h>

#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"

#include "board.h"
#include "sysclk.h"
#include "intc.h"
#include "gpio.h"
#include "spi.h"

/* SPI path name index. */
#define serSPIEXT			0		//!< SPI1 CS0 for external SPI (CN6).
#define serSPILCD			1		//!< SPI0 CS0 for LCD interface.
#define serSPIKEY_W			2		//!< SPI0 CS1 for Keypad write interface.
#define serSPIKEY_R			3		//!< SPI0 CS2 for keypad load interface.
#define serSPIUARTCTRL		4		//!< SPI1 CS1 for controlling UART interface.
#define serSPIROM			5		//!< SPI1 CS2 for serial flash ROM/ EEPROM interface.
#define serSPISDMMC			6		//!< SPI1 CS3 for SD/MMC interface.

/* Delay between chip select. */
#define SPI_DELAY_BCS			12

static const char spi_path_name[] = "SPI";

static spi_peripheral_descriptors_t spi_peripheral_desc[7] =
{
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00},
	{.path_name = spi_path_name, .path_index = 16, .port_state = 0, .recved_size = NULL, .transfer_mode = 0x00}
};

/* Available SPI port name. */
static const char spi_port_name[][12] = {"SPIEXT", "SPILCD", "SPIKEY_W", "SPIKEY_R", "SPIUARTCTRL", "SPIROM", "SPISDMMC"};

static spi_channel_options_t spi_opts[7];

//! \internal Initialization flag
static bool spi_already_initialized[2] = {false, false};
	
//! \internal SPI master/slave mode flag of external SPI
static bool ext_spi_is_master = true;

//! \internal Pointer to the instance of the SPI registers.
static volatile avr32_spi_t *spi0_inst = DEFAULT_SPI0;
static volatile avr32_spi_t *spi1_inst = DEFAULT_SPI1;
//! \internal Pointer to the data byte transmit buffer.
static const volatile uint8_t  *spi_tx0_data_byte = NULL;
static const volatile uint8_t  *spi_tx1_data_byte = NULL;
//! \internal Pointer to the data word transmit buffer.
static const volatile uint16_t  *spi_tx0_data_word = NULL;
static const volatile uint16_t  *spi_tx1_data_word = NULL;
//! \internal Pointer to the data byte receive buffer.
static volatile uint8_t *spi_rx0_data_byte = NULL;
static volatile uint8_t *spi_rx1_data_byte = NULL;
//! \internal Pointer to the data word receive buffer.
static volatile uint16_t *spi_rx0_data_word = NULL;
static volatile uint16_t *spi_rx1_data_word = NULL;
//! \internal Remaining number of data to transmit.
static volatile uint32_t spi_tx0_nb_count = 0;
static volatile uint32_t spi_tx1_nb_count = 0;
//! \internal Total number of data to receive.
static volatile uint32_t spi_rx0_nb_received = 0;
static volatile uint32_t spi_rx1_nb_received = 0;
//! \internal Remaining number of data to receive.
static volatile uint32_t spi_rx0_nb_count = 0;
static volatile uint32_t spi_rx1_nb_count = 0;
//! \internal Bit length bigger than 8-bit.
static bool datawidth0_is_bigger8bit = false;
static bool datawidth1_is_bigger8bit = false;
//! \internal Write direction data.
static bool operation0_is_write = false;
static bool operation1_is_write = false;


#define serial_SPI0_IRQ			AVR32_SPI0_IRQ
#define serial_SPI1_IRQ			AVR32_SPI1_IRQ

#define SPI0_INT_PRIORITY		AVR32_INTC_INT2
#define SPI1_INT_PRIORITY		AVR32_INTC_INT2


//! The SPI semaphore.
static xSemaphoreHandle xSPI0Semaphore, xSPI1Semaphore;


/** \brief Interrupt service routine of SPI0.
 *
 * \return _xHigherPriorityTaskWoken	Flag for context switching.
 *
 */
__attribute__((__noinline__)) static long prvSPI0_ISR_NonNakedBehaviour(void)
{
	long _xHigherPriorityTaskWoken = pdFALSE;
	uint32_t _ulStatus;
	uint16_t _sShort;

	/* What caused the interrupt? */
	_ulStatus = (spi0_inst->sr & spi0_inst->imr);

	if(_ulStatus & AVR32_SPI_SR_OVRES_MASK) {
		/* The interrupt was caused by over run on SPI.
		Empty the FIFO buffer. */
		spi0_inst->cr = AVR32_SPI_CR_FLUSHFIFO_MASK;

		/* Using data memory barriers in the IRQ handler to make sure that:
		- the request has been dis-asserted, and that
		- the (potential) write in the idr has been done
		... before returning from the handler */
		spi0_inst->sr;

		/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
		should perform a vTaskSwitchContext(). */
		return (_xHigherPriorityTaskWoken);
	}
	else if(_ulStatus & AVR32_SPI_SR_RDRF_MASK) {
		/* The interrupt was caused by the receiver getting data. */
		_sShort = (uint16_t)((spi0_inst->rdr & AVR32_SPI_RDR_RD_MASK) << AVR32_SPI_RDR_RD_OFFSET);

		if(spi_rx0_nb_count > 0) {
			/* Decrease remaining number data to receive. */
			spi_rx0_nb_count--;

			if(datawidth0_is_bigger8bit == true) {
				if(spi_rx0_data_word != NULL) {
					/* Copy data to receiver buffer. */
					*spi_rx0_data_word = _sShort;
					/* Increase pointer of receiver data buffer. */
					spi_rx0_data_word++;
					/* Increase received word number. */
					spi_rx0_nb_received++;
				}
			}
			else {
				if(spi_rx0_data_byte != NULL) {
					/* Copy data to receiver buffer. */
					*spi_rx0_data_byte = (uint8_t)_sShort;
					/* Increase pointer of receiver data buffer. */
					spi_rx0_data_byte++;
					/* Increase received byte number. */
					spi_rx0_nb_received++;
				}
			}
		}
		else {
			/* Disable receiving interrupt and over run interrupt. */
			spi0_inst->idr = (AVR32_SPI_IDR_RDRF_MASK | AVR32_SPI_IDR_OVRES_MASK);
			/* Reset receiving address. */
			if(datawidth0_is_bigger8bit == true) {
				spi_rx0_data_word = NULL;
			}
			else {
				spi_rx0_data_byte = NULL;
			}
		}
	}
	else if(_ulStatus & AVR32_SPI_SR_TDRE_MASK) {
		/* The interrupt was caused by the TDR becoming empty.  Are there any
		more characters to transmit? */

		/* No more word to transmit. */
		if(spi_tx0_nb_count == 0) {
			/* Finish the receive operation. */
			spi0_inst->idr = AVR32_SPI_IDR_TDRE_MASK;
		}
		else {
			/* Check data width. */
			if(datawidth0_is_bigger8bit == true) {
				/* Check operation mode. */
				if(operation0_is_write == true) {
					/* Copy the data. */
					_sShort = *spi_tx0_data_word;
					/* Increase the pointer of transmitter data buffer. */
					spi_tx0_data_word++;
				}
				else {
					/* Dummy data. */
					_sShort = 0xFFFF;
				}
			}
			else {
				/* Check operation mode. */
				if(operation0_is_write == true) {
					/* Copy the data. */
					_sShort = (uint16_t)*spi_tx0_data_byte;
					/* Increase the pointer of transmitter data buffer. */
					spi_tx0_data_byte++;
				}
				else {
					/* Dummy data. */
					_sShort = 0xFFFF;
				}
			}

			/* Put the data in the Transmit Holding Register. */
			spi0_inst->tdr = (uint32_t)((_sShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
			/* Decrease transmitted word number. */
			spi_tx0_nb_count--;
		}
	}
	else if(_ulStatus & AVR32_SPI_SR_TXEMPTY_MASK) {
		/* The interrupt was caused by transmission complete.
		Disable Tx empty interrupt. */
		spi0_inst->idr = AVR32_SPI_IDR_TXEMPTY_MASK;
		portENTER_CRITICAL();
		xSemaphoreGiveFromISR(xSPI0Semaphore, &_xHigherPriorityTaskWoken);
		portEXIT_CRITICAL();
	}
	

	/* Using data memory barriers in the IRQ handler to make sure that:
	- the request has been dis-asserted, and that
	- the (potential) write in the idr has been done
	... before returning from the handler */
	spi0_inst->sr;

	/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
	should perform a vTaskSwitchContext(). */
	return (_xHigherPriorityTaskWoken);
}


/** \brief Interrupt service routine of SPI1.
 *
 * \return _xHigherPriorityTaskWoken	Flag for context switching.
 *
 */
__attribute__((__noinline__)) static long prvSPI1_ISR_NonNakedBehaviour(void)
{
	long _xHigherPriorityTaskWoken = pdFALSE;
	uint32_t _ulStatus;
	uint16_t _sShort;

	/* What caused the interrupt? */
	_ulStatus = (spi1_inst->sr & spi1_inst->imr);

	if(_ulStatus & AVR32_SPI_SR_OVRES_MASK) {
		/* The interrupt was caused by over run on SPI.
		Empty the FIFO buffer. */
		spi1_inst->cr = AVR32_SPI_CR_FLUSHFIFO_MASK;

		/* Using data memory barriers in the IRQ handler to make sure that:
		- the request has been dis-asserted, and that
		- the (potential) write in the idr has been done
		... before returning from the handler */
		spi1_inst->sr;

		/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
		should perform a vTaskSwitchContext(). */
		return (_xHigherPriorityTaskWoken);
	}
	else if(_ulStatus & AVR32_SPI_SR_RDRF_MASK) {
		/* The interrupt was caused by the receiver getting data. */
		_sShort = (uint16_t)((spi1_inst->rdr & AVR32_SPI_RDR_RD_MASK) << AVR32_SPI_RDR_RD_OFFSET);

		if(spi_rx1_nb_count > 0) {
			/* Decrease remaining number data to receive. */
			spi_rx1_nb_count--;

			if(datawidth1_is_bigger8bit == true) {
				if(spi_rx1_data_word != NULL) {
					/* Copy data to receiver buffer. */
					*spi_rx1_data_word = _sShort;
					/* Increase pointer of receiver data buffer. */
					spi_rx1_data_word++;
					/* Increase received word number. */
					spi_rx1_nb_received++;
				}
			}
			else {
				if(spi_rx1_data_byte != NULL) {
					/* Copy data to receiver buffer. */
					*spi_rx1_data_byte = (uint8_t)_sShort;
					/* Increase pointer of receiver data buffer. */
					spi_rx1_data_byte++;
					/* Increase received byte number. */
					spi_rx1_nb_received++;
				}
			}
		}
		else {
			/* Disable receiving interrupt and over run interrupt. */
			spi1_inst->idr = (AVR32_SPI_IDR_RDRF_MASK | AVR32_SPI_IDR_OVRES_MASK);
			/* Reset receiving address. */
			if(datawidth1_is_bigger8bit == true) {
				spi_rx1_data_word = NULL;
			}
			else {
				spi_rx1_data_byte = NULL;
			}
		}
	}
	else if(_ulStatus & AVR32_SPI_SR_TDRE_MASK) {
		/* The interrupt was caused by the TDR becoming empty.  Are there any
		more characters to transmit? */

		/* No more word to transmit. */
		if(spi_tx1_nb_count == 0) {
			/* Finish the receive operation. */
			spi1_inst->idr = AVR32_SPI_IDR_TDRE_MASK;
		}
		else {
			/* Check data width. */
			if(datawidth1_is_bigger8bit == true) {
				/* Check operation mode. */
				if(operation1_is_write == true) {
					/* Copy the data. */
					_sShort = *spi_tx1_data_word;
					/* Increase the pointer of transmitter data buffer. */
					spi_tx1_data_word++;
				}
				else {
					/* Dummy data. */
					_sShort = 0xFFFF;
				}
			}
			else {
				/* Check operation mode. */
				if(operation1_is_write == true) {
					/* Copy the data. */
					_sShort = (uint16_t)*spi_tx1_data_byte;
					/* Increase the pointer of transmitter data buffer. */
					spi_tx1_data_byte++;
				}
				else {
					/* Dummy data. */
					_sShort = 0xFFFF;
				}
			}

			/* Put the data in the Transmit Holding Register. */
			spi1_inst->tdr = (uint32_t)((_sShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
			/* Decrease transmitted word number. */
			spi_tx1_nb_count--;
		}
	}
	else if(_ulStatus & AVR32_SPI_SR_TXEMPTY_MASK) {
		/* The interrupt was caused by transmission complete.
		Disable TX empty interrupt. */
		spi1_inst->idr = AVR32_SPI_IDR_TXEMPTY_MASK;

		portENTER_CRITICAL();
		xSemaphoreGiveFromISR(xSPI1Semaphore, &_xHigherPriorityTaskWoken);
		portEXIT_CRITICAL();
	}

	/* Using data memory barriers in the IRQ handler to make sure that:
	- the request has been dis-asserted, and that
	- the (potential) write in the idr has been done
	... before returning from the handler */
	spi1_inst->sr;

	/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
	should perform a vTaskSwitchContext(). */
	return (_xHigherPriorityTaskWoken);
}

/** \brief Interrupt service routine of serial SPI0.
 */
__attribute__((__naked__)) static void prvSPI0_ISR(void)
{
	/* This ISR can cause a context switch, so the first statement must be a
	call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
	variable declarations. */
	portENTER_SWITCHING_ISR();

	prvSPI0_ISR_NonNakedBehaviour();

	/* Exit the ISR.  If a task was woken by either a character being received
	or transmitted then a context switch will occur. */
	portEXIT_SWITCHING_ISR();
}


/** \brief Interrupt service routine of serial SPI1.
 */
__attribute__((__naked__)) static void prvSPI1_ISR(void)
{
	/* This ISR can cause a context switch, so the first statement must be a
	call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
	variable declarations. */
	portENTER_SWITCHING_ISR();

	prvSPI1_ISR_NonNakedBehaviour();

	/* Exit the ISR.  If a task was woken by either a character being received
	or transmitted then a context switch will occur. */
	portEXIT_SWITCHING_ISR();
}



/** \brief Reset the SPI.
 *
 * \param spi       Base address of the SPI instance.
 *
 */
static void spi_reset(volatile avr32_spi_t *_spi)
{
	bool global_interrupt_enabled = cpu_irq_is_enabled();

	/* Disable all SPI interrupts.
	 Interrupts needed should be set explicitly on every reset. */
	if (global_interrupt_enabled) cpu_irq_disable();
	_spi->idr = 0xFFFFFFFF;
	_spi->sr;
	_spi->cr = AVR32_SPI_CR_SWRST_MASK;
	if (global_interrupt_enabled) cpu_irq_enable();

}

/** \brief Empty FIFO of the SPI.
 *
 * \param spi       Base address of the SPI instance.
 *
 */
static inline void spi_flush_fifo(volatile avr32_spi_t *_spi)
{
	_spi->cr = AVR32_SPI_CR_FLUSHFIFO_MASK;
}

/** \brief Set Master Mode of the SPI.
 *
 * \param spi         Base address of the SPI instance.
 */
static inline void spi_set_master_mode(volatile avr32_spi_t *_spi)
{
	_spi->MR.mstr = 1;
}

/** \brief Disable Modfault of the SPI.
 *
 * \param spi Base address of the SPI instance.
 */
static inline void spi_disable_modfault(volatile avr32_spi_t *_spi)
{
	_spi->MR.modfdis = 1;
}

/** \brief Disable Loopback of the SPI.
 *
 * \param spi Base address of the SPI instance.
 */
static inline void spi_disable_loopback(volatile avr32_spi_t *_spi)
{
	_spi->MR.llb = 0;
}

/** \brief Disable Variable Chip Select of the SPI.
 *
 * \param spi         Base address of the SPI instance.
 */
static inline void spi_disable_variable_chipselect(volatile avr32_spi_t *_spi)
{
	_spi->MR.ps = 0;
}

/** \brief Set Chip Select of the SPI.
 *
 * \param spi         Base address of the SPI instance.
 * \param chip_select Chip Select.
 */
static inline void spi_set_chipselect(volatile avr32_spi_t *_spi, uint8_t _chip_select)
{
	_spi->MR.pcs = _chip_select;
}

/** \brief Disable Chip Select Decoding of the SPI.
 *
 * \param spi Base address of the SPI instance.
 */
static inline void spi_disable_chipselect_decoding(volatile avr32_spi_t *_spi)
{
	_spi->MR.pcsdec = 0;
}

/** \brief Enable FIFO in reception of the SPI.
 *
 * \param spi Base address of the SPI instance.
 */
static inline void spi_enable_recv_fifo(volatile avr32_spi_t *_spi)
{
	_spi->MR.rxfifoen = 1;
}

/** \brief Set Delay Between Chip Selects of the SPI.
 *
 * \param spi         Base address of the SPI instance.
 * \param delay       Delay.
 */
static inline void spi_set_delay(volatile avr32_spi_t *_spi, uint8_t _delay)
{
	_spi->MR.dlybcs = _delay;
}

/** \brief Set Delay Between Consecutive Transfer on a Chip Selects of the SPI.
 *
 * \param _spi         Base address of the SPI instance.
 * \param _chip_select Chip Select.
 * \param _delay       Delay.
 */

static inline void spi_set_chip_delay_bct(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _delay)
{

	switch (_chip_select) {
		case 0:
			_spi->CSR0.dlybct = _delay;
		break;

		case 1:
			_spi->CSR1.dlybct = _delay;
		break;

		case 2:
			_spi->CSR2.dlybct = _delay;
		break;

		case 3:
			_spi->CSR3.dlybct = _delay;
		break;

		default: break;
	}
}


/** \brief Set Delay Before SPCK on a Chip Selects of the SPI.
 *
 * \param _spi         Base address of the SPI instance.
 * \param _chip_select Chip Select.
 * \param _delay       Delay.
 */
static inline void spi_set_chip_delay_bs(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _delay)
{
	switch (_chip_select) {
		case 0:
			_spi->CSR0.dlybs = _delay;
		break;

		case 1:
			_spi->CSR1.dlybs = _delay;
		break;

		case 2:
			_spi->CSR2.dlybs = _delay;
		break;

		case 3:
			_spi->CSR3.dlybs = _delay;
		break;

		default: break;
	}
}


/** \brief Set bit per transfer on a Chip Selects of the SPI.
 *
 * \param _spi         Base address of the SPI instance.
 * \param _chip_select Chip Select.
 * \param _len         Bits per Transfer [8...16].
 */
static inline void spi_set_chip_bits_per_transfer(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _len)
{

	switch (_chip_select) {
		case 0:
			_spi->CSR0.bits = _len - 8;
		break;

		case 1:
			_spi->CSR1.bits = _len - 8;
		break;

		case 2:
			_spi->CSR2.bits = _len - 8;
		break;

		case 3:
			_spi->CSR3.bits = _len - 8;
		break;

		default: break;
	}
}


/** \brief Set baudrate for a Chip Selects of the SPI.
 *
 * \param spi         Base address of the SPI instance.
 * \param chip_select Chip Select.
 * \param scbr        Baudrate Register.
 */
static inline void spi_set_chip_baudrate_register(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _scbr)
{
	switch (_chip_select) {
		case 0:
			_spi->CSR0.scbr = _scbr;
		break;

		case 1:
			_spi->CSR1.scbr  = _scbr;
		break;

		case 2:
			_spi->CSR2.scbr  = _scbr;
		break;

		case 3:
			_spi->CSR3.scbr  = _scbr;
		break;

		default: break;
	}
}


/** \brief Enable Active mode of a Chip Selects of the SPI.
 *
 * \param spi			Base address of the SPI instance.
 * \param chip_select	Chip Select.
 * \param _csaat		Flag of hip select active after transfer
 */
static inline void spi_enable_chip_active_mode(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _csaat)
{
	switch (_chip_select) {
		case 0:
			_spi->CSR0.csaat = _csaat;
		break;

		case 1:
			_spi->CSR1.csaat = _csaat;
		break;

		case 2:
			_spi->CSR2.csaat = _csaat;
		break;

		case 3:
			_spi->CSR3.csaat = _csaat;
		break;

		default: break;
	}
}


/** \brief Set Mode of the SPI.
 *
 * \param _spi			Base address of the SPI instance.
 * \param _chip_select	Chip Select.
 * \param _flags		SPI Mode.
 */
static inline void spi_set_chip_mode(volatile avr32_spi_t *_spi, uint8_t _chip_select, uint8_t _flags)
{
	switch (_chip_select) {
		case SPI_MODE_0:
			_spi->CSR0.cpol = _flags >> 1;
			_spi->CSR0.ncpha = (_flags & 0x1) ^ 0x1;
		break;

		case SPI_MODE_1:
			_spi->CSR1.cpol  = _flags >> 1;
			_spi->CSR1.ncpha = (_flags & 0x1) ^ 0x1;
		break;

		case SPI_MODE_2:
			_spi->CSR2.cpol  = _flags >> 1;
			_spi->CSR2.ncpha = (_flags & 0x1) ^ 0x1;
		break;

		case SPI_MODE_3:
			_spi->CSR3.cpol  = _flags >> 1;
			_spi->CSR3.ncpha = (_flags & 0x1) ^ 0x1;
		break;

		default: break;
	}
}


/** \brief Calculates the baudrate divider.
 *
 * \param _baudrate Baudrate value.
 * \param _pb_hz  SPI module input clock frequency (PBA clock, Hz).
 *
 * \return Divider or error code.
 *   \retval >=0  Success.
 *   \retval  <0  Error.
 */
static int16_t getBaudDiv(const uint32_t _baudrate, uint32_t _pb_hz)
{
	uint32_t _baudDiv = div_ceil(_pb_hz, _baudrate);

	if (_baudDiv <= 0 || _baudDiv > 255) {
		return -1;
	}

	return _baudDiv;
}


/** \brief Enable SPI interface.
 *
 * \param	_spi	Base address of the SPI instance.
 *
 */
static void spi_enable(volatile avr32_spi_t *_spi)
{
	_spi->cr = AVR32_SPI_CR_SPIEN_MASK;
}


/** \brief Disable SPI interface.
 *
 * \param	_spi	Base address of the SPI instance.
 *
 */
/*
static void spi_disable(volatile avr32_spi_t *_spi)
{
	_spi->cr = AVR32_SPI_CR_SPIDIS_MASK;
}
*/

/** \brief Check if SPI is enabled.
 *
 * \param	_spi	Base address of the SPI instance.
 *
 * \return Status.
 *	\retval	true	SPI is enabled.
 *	\retval	false	SPI is disabled.
 *
 */
static bool spi_is_enabled(volatile avr32_spi_t *_spi)
{
	return (_spi->sr & AVR32_SPI_SR_SPIENS_MASK) != 0;
}


/** \brief Start transmit data out to the port.
 *  \param _spi_desc				Pointer to peripheral descriptor.
 *  \param _ptrData					Pointer to data buffer want to be transmitted.
 *  \param _size_t					Number of byte/word to be transmitted.
 *  \param _datawidth8bitmore		true = word transfer, false = byte transfer.
 *
 *  \return Status.
 */
static spi_status_code_t spi_master_write_start(spi_peripheral_descriptors_t *_spi_desc, const void *_ptrData, uint32_t _size_t, bool _datawidth8bitmore)
{
	spi_status_code_t _ret_status = SPI_ERR_INVALID_ARG;
	uint16_t _xShort;

	switch(_spi_desc->path_index) {
		case serSPILCD:
		case serSPIKEY_R:
		case serSPIKEY_W:
			/* Determine operation direction. */
			if(_ptrData != NULL) {
				/* Operation is write. */
				operation0_is_write = true;

				/* Check data width. */
				if(_datawidth8bitmore == true) {
					/* Get the pointer of data. */
					spi_tx0_data_word = (const volatile uint16_t *)_ptrData;
				}
				else {
					/* Get the pointer of data. */
					spi_tx0_data_byte = (const volatile uint8_t *)_ptrData;
				}
			}
			else {
				/* Operation is read. */
				operation0_is_write = false;

				spi_rx0_nb_received = 0;
				_spi_desc->recved_size = (uint32_t *)&spi_rx0_nb_received;
				spi_rx0_nb_count = _size_t;
			}

			/* Get the length of transfer count. */
			spi_tx0_nb_count = _size_t;

			datawidth0_is_bigger8bit = _datawidth8bitmore;
			
			/* Write direction. */
			if(operation0_is_write == true) {
				/* Interrupt driven. */
				if(_spi_desc->transfer_mode & 0x01) {
					/* Disable receive and overrun interrupt. */
					spi0_inst->idr = (AVR32_SPI_IDR_RDRF_MASK | AVR32_SPI_IDR_OVRES_MASK);
					/* Enable transmit data ready and transmit empty interrupt. */
					spi0_inst->ier = (AVR32_SPI_IER_TDRE_MASK | AVR32_SPI_IER_TXEMPTY_MASK);
				}
				/* Polled driven. */
				else {
					while(spi_tx0_nb_count) {
						/* Wait transmit data register ready. */
						while(!(spi0_inst->sr & AVR32_SPI_SR_TDRE_MASK));
						
						/* Check data width. */
						if(datawidth0_is_bigger8bit == true) {
							/* Get data from buffer. */
							_xShort = *spi_tx0_data_word;
							/* Increase Pointer data. */
							spi_tx0_data_word++;						
						}
						else {
							/* Get data from buffer. */
							_xShort = (uint16_t) *spi_tx0_data_byte;
							/* Increase Pointer data. */
							spi_tx0_data_byte++;						
						}
						
						/* Decrease number word/byte counting. */
						spi_tx0_nb_count--;
						/* Send data to transmit data register. */
						spi0_inst->tdr = (uint32_t)((_xShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
							
						/* Wait until transmission complete. */
						while(!(spi0_inst->sr & AVR32_SPI_SR_TXEMPTY_MASK));
					}
				}
			}
			/* Receive direction. */
			else {
				/* Clear Receive Buffer. */
				spi_flush_fifo(spi0_inst);

				/* Interrupt driven. */
				if(_spi_desc->transfer_mode & 0x02) {
					/* Enable receive and overrun interrupt. */
					spi0_inst->ier = (AVR32_SPI_IER_RDRF_MASK | AVR32_SPI_IER_OVRES_MASK | AVR32_SPI_IER_TDRE_MASK | AVR32_SPI_IER_TXEMPTY_MASK);
					while(spi_rx0_nb_count) {
						vTaskDelay(20);
					}
				}
				/* Pollen driven. */
				else {
					while(spi_rx0_nb_count) {
						/* Check overrun error. */
						if(spi0_inst->sr & AVR32_SPI_SR_OVRES_MASK) {
							/* Clear Receive Buffer. */
							spi_flush_fifo(spi0_inst);
							/* Free peripheral. */
							xSemaphoreGive(xSPI0Semaphore);
							
							return SPI_ERR_FLUSHED;
						}
						
						/* Wait transmit data register ready. */
						while(!(spi0_inst->sr & AVR32_SPI_SR_TDRE_MASK));					
						
						/* Dummy data. */
						_xShort = 0xFFFF;
							
						/* Decrease number word/byte counting. */
						spi_tx0_nb_count--;
						/* Send data to transmit data register. */
						spi0_inst->tdr = (uint32_t)((_xShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
						
						/* Wait until transmission complete. */
						while(!(spi0_inst->sr & AVR32_SPI_SR_TXEMPTY_MASK));
						
						/* Get received data. */
						_xShort = (uint16_t)((spi0_inst->rdr & AVR32_SPI_RDR_RD_MASK) << AVR32_SPI_RDR_RD_OFFSET);
						
						/* Check if receiving mode. */
						if (spi_rx0_nb_count > 0) {
							if(datawidth0_is_bigger8bit == true) {
								/* Copy receiving data to the receiver buffer. */
								*spi_rx0_data_word = _xShort;
								/* Increase pointer of receiver buffer. */
								spi_rx0_data_word++;
							}
							else {
								/* Copy receiving data to the receiver buffer. */
								*spi_rx0_data_byte = (uint8_t)_xShort;
								/* Increase pointer of receiver buffer. */
								spi_rx0_data_byte++;
							}
							/* Increase number of received data. */
							spi_rx0_nb_received++;
							/* Decrease remaining receive data. */
							spi_rx0_nb_count--;
						}
					}
				}
			}
			
			/* Free the peripheral. */
			xSemaphoreGive(xSPI0Semaphore);
			_ret_status = SPI_STATUS_OK;	
		break;
		
		case serSPIEXT:
		case serSPIUARTCTRL:
		case serSPIROM:
		case serSPISDMMC:
			/* Determine operation direction. */
			if(_ptrData != NULL) {
				/* Operation is write. */
				operation1_is_write = true;

				/* Check data width. */
				if(_datawidth8bitmore == true) {
					/* Get the pointer of data. */
					spi_tx1_data_word = (const volatile uint16_t *)_ptrData;
				}
				else {
					/* Get the pointer of data. */
					spi_tx1_data_byte = (const volatile uint8_t *)_ptrData;
				}
			}
			else {
				/* Operation is read. */
				operation1_is_write = false;

				spi_rx1_nb_received = 0;
				_spi_desc->recved_size = (uint32_t *)&spi_rx1_nb_received;
				spi_rx1_nb_count = _size_t;
			}

			/* Get the length to be transmitted. */
			spi_tx1_nb_count = _size_t;

			datawidth1_is_bigger8bit = _datawidth8bitmore;
			
			/* Write direction. */
			if(operation1_is_write == true) {
				/* Interrupt driven. */
				if(_spi_desc->transfer_mode & 0x01) {
					/* Disable receive and overrun interrupt. */
					spi1_inst->idr = (AVR32_SPI_IDR_RDRF_MASK | AVR32_SPI_IDR_OVRES_MASK);
					/* Enable transmit data ready and transmit empty interrupt. */
					spi1_inst->ier = (AVR32_SPI_IER_TDRE_MASK | AVR32_SPI_IER_TXEMPTY_MASK);
				}
				/* Polled driven. */
				else {
					while(spi_tx1_nb_count) {
						/* Wait transmit data register ready. */
						while(!(spi1_inst->sr & AVR32_SPI_SR_TDRE_MASK));
						
						/* Check data width. */
						if(datawidth1_is_bigger8bit == true) {
							/* Get data from buffer. */
							_xShort = *spi_tx1_data_word;
							/* Increase Pointer data. */
							spi_tx1_data_word++;
						}
						else {
							/* Get data from buffer. */
							_xShort = (uint16_t) *spi_tx1_data_byte;
							/* Increase Pointer data. */
							spi_tx1_data_byte++;
						}
						
						/* Decrease number word/byte counting. */
						spi_tx1_nb_count--;
						/* Send data to transmit data register. */
						spi1_inst->tdr = (uint32_t)((_xShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
						
						/* Wait until transmission complete. */
						while(!(spi1_inst->sr & AVR32_SPI_SR_TXEMPTY_MASK));
					}
				}
			}
			/* Receive direction. */
			else {
				/* Clear Receive Buffer. */
				spi_flush_fifo(spi1_inst);

				/* Interrupt driven. */
				if(_spi_desc->transfer_mode & 0x02) {
					/* Enable receive and overrun interrupt. */
					spi1_inst->ier = (AVR32_SPI_IER_RDRF_MASK | AVR32_SPI_IER_OVRES_MASK | AVR32_SPI_IER_TDRE_MASK | AVR32_SPI_IER_TXEMPTY_MASK);
					while(spi_rx1_nb_count) {
						vTaskDelay(20);
					}
				}
				/* Pollen driven. */
				else {
					while(spi_rx1_nb_count) {
						/* Check overrun error. */
						if(spi1_inst->sr & AVR32_SPI_SR_OVRES_MASK) {
							/* Clear Receive Buffer. */
							spi_flush_fifo(spi1_inst);
							/* Free peripheral. */
							xSemaphoreGive(xSPI1Semaphore);
							
							return SPI_ERR_FLUSHED;
						}
						
						/* Wait transmit data register ready. */
						while(!(spi1_inst->sr & AVR32_SPI_SR_TDRE_MASK));
						
						/* Dummy data. */
						_xShort = 0xFFFF;
						
						/* Decrease number word/byte counting. */
						spi_tx1_nb_count--;
						/* Send data to transmit data register. */
						spi1_inst->tdr = (uint32_t)((_xShort << AVR32_SPI_TDR_TD_OFFSET) & AVR32_SPI_TDR_TD_MASK);
						
						/* Wait until transmission complete. */
						while(!(spi1_inst->sr & AVR32_SPI_SR_TXEMPTY_MASK));
						
						/* Get received data. */
						_xShort = (uint16_t)((spi1_inst->rdr & AVR32_SPI_RDR_RD_MASK) << AVR32_SPI_RDR_RD_OFFSET);
						
						/* Check if receiving mode. */
						if (spi_rx1_nb_count > 0) {
							if(datawidth1_is_bigger8bit == true) {
								/* Copy receiving data to the receiver buffer. */
								*spi_rx1_data_word = _xShort;
								/* Increase pointer of receiver buffer. */
								spi_rx1_data_word++;
							}
							else {
								/* Copy receiving data to the receiver buffer. */
								*spi_rx1_data_byte = (uint8_t)_xShort;
								/* Increase pointer of receiver buffer. */
								spi_rx1_data_byte++;
							}
							/* Increase number of received data. */
							spi_rx1_nb_received++;
							/* Decrease remaining receive data. */
							spi_rx1_nb_count--;
						}
					}
				}
			}

			/* Free the peripheral. */
			xSemaphoreGive(xSPI1Semaphore);

			_ret_status = SPI_STATUS_OK;
		break;
		
		default: break;
		
	}

	return _ret_status;
}

/** \brief Start receive data from the port.
 *  \param _spi_desc				Pointer to peripheral descriptor.
 *  \param _pbuf					Pointer to buffer where data will be stored.
 *  \param _size_t					Number of byte/word to be received.
 *  \param _datawidth8bitmore		true = word transfer, false = byte transfer.
 *
 *  \return Status.
 */
static spi_status_code_t spi_master_read_start(spi_peripheral_descriptors_t *_spi_desc, void *_pbuf, uint32_t _size_t, bool _datawidth8bitmore)
{
	const void *_ptrDummy = NULL;
	spi_status_code_t _ret_status = SPI_ERR_INVALID_ARG;

	if(_pbuf == NULL) return _ret_status;

	switch(_spi_desc->path_index) {
		case serSPILCD:
		case serSPIKEY_W:
		case serSPIKEY_R:
			datawidth0_is_bigger8bit = _datawidth8bitmore;
			if(datawidth0_is_bigger8bit == true) {
				spi_rx0_data_word = (volatile uint16_t *)_pbuf;
				return spi_master_write_start(_spi_desc, _ptrDummy, _size_t, true);
			}
			else {
				spi_rx0_data_byte = (volatile uint8_t *)_pbuf;
				return spi_master_write_start(_spi_desc, _ptrDummy, _size_t, false);
			}

			_ret_status = SPI_STATUS_OK;
		break;
		
		case serSPIEXT:
		case serSPIUARTCTRL:
		case serSPIROM:
		case serSPISDMMC:
			datawidth1_is_bigger8bit = _datawidth8bitmore;
			if(datawidth1_is_bigger8bit == true) {
				spi_rx1_data_word = (volatile uint16_t *)_pbuf;
				return spi_master_write_start(_spi_desc, _ptrDummy, _size_t, true);
			}
			else {
				spi_rx1_data_byte = (volatile uint8_t *)_pbuf;
				return spi_master_write_start(_spi_desc, _ptrDummy, _size_t, false);
			}

			_ret_status = SPI_STATUS_OK;
		break;
		
		default: break;
		
	}
	
	return _ret_status;
}


/** \brief Setup the SPI interface as master.
 *
 * \param _spi_desc		Pointer to peripheral descriptor.
 * \param _opts			SPI setup options.
 *
 * \return SPI status
 *	\retval	SPI_STATUS_OK		Setup is OK.
 *	\retval	SPI_ERR_INVALID_ARG	Invalid SPI port.
 *
 */
static spi_status_code_t spi_master_chip_setup(spi_peripheral_descriptors_t *_spi_desc, const spi_channel_options_t *_opts)
{
	spi_status_code_t _ret_status = SPI_ERR_INVALID_ARG;

	switch(_spi_desc->path_index) {
		case serSPILCD:
		case serSPIKEY_W:
		case serSPIKEY_R:
			spi_set_master_mode(spi0_inst);
			spi_disable_modfault(spi0_inst);
			spi_disable_loopback(spi0_inst);
			spi_set_chipselect(spi0_inst, (1 << AVR32_SPI_MR_PCS_SIZE) - 1);
			spi_disable_variable_chipselect(spi0_inst);
			spi_disable_chipselect_decoding(spi0_inst);
			spi_set_delay(spi0_inst, SPI_DELAY_BCS);
			
			switch(_spi_desc->path_index)
			{
				case serSPILCD:
					spi_set_chip_delay_bct(spi0_inst, LCD_SPI_NPCS, _opts->delay_bct);
					spi_set_chip_delay_bs(spi0_inst, LCD_SPI_NPCS, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi0_inst, LCD_SPI_NPCS, _opts->bits);
					spi_set_chip_baudrate_register(spi0_inst, LCD_SPI_NPCS, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi0_inst)));
					spi_enable_chip_active_mode(spi0_inst, LCD_SPI_NPCS, _opts->keep_actv);
					spi_set_chip_mode(spi0_inst, LCD_SPI_NPCS, _opts->spi_mode);
				break;
				
				case serSPIKEY_W:
					spi_set_chip_delay_bct(spi0_inst, KEY_SPI_NPCS_WR, _opts->delay_bct);
					spi_set_chip_delay_bs(spi0_inst, KEY_SPI_NPCS_WR, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi0_inst, KEY_SPI_NPCS_WR, _opts->bits);
					spi_set_chip_baudrate_register(spi0_inst, KEY_SPI_NPCS_WR, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi0_inst)));
					spi_enable_chip_active_mode(spi0_inst, KEY_SPI_NPCS_WR, _opts->keep_actv);
					spi_set_chip_mode(spi0_inst, KEY_SPI_NPCS_WR, _opts->spi_mode);
				break;

				case serSPIKEY_R:
					spi_set_chip_delay_bct(spi0_inst, KEY_SPI_NPCS_RD, _opts->delay_bct);
					spi_set_chip_delay_bs(spi0_inst, KEY_SPI_NPCS_RD, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi0_inst, KEY_SPI_NPCS_RD, _opts->bits);
					spi_set_chip_baudrate_register(spi0_inst, KEY_SPI_NPCS_RD, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi0_inst)));
					spi_enable_chip_active_mode(spi0_inst, KEY_SPI_NPCS_RD, _opts->keep_actv);
					spi_set_chip_mode(spi0_inst, KEY_SPI_NPCS_RD, _opts->spi_mode);
				break;

				default: break;
			}

			if(!xSPI0Semaphore) {
				/* Create the SPI semaphore. */
				vSemaphoreCreateBinary(xSPI0Semaphore);
				if(!xSPI0Semaphore) {
					while(1);
				}
			}

			if(!spi_is_enabled(spi0_inst)) spi_enable(spi0_inst);

			_ret_status = SPI_STATUS_OK;
		break;
		
		case serSPIEXT:
		case serSPIUARTCTRL:
		case serSPIROM:
		case serSPISDMMC:
			spi_set_master_mode(spi1_inst);
			spi_disable_modfault(spi1_inst);
			spi_disable_loopback(spi1_inst);
			spi_set_chipselect(spi1_inst, (1 << AVR32_SPI_MR_PCS_SIZE) - 1);
			spi_disable_variable_chipselect(spi1_inst);
			spi_disable_chipselect_decoding(spi1_inst);
			spi_set_delay(spi1_inst, SPI_DELAY_BCS);
			
			switch(_spi_desc->path_index)
			{
				case serSPIEXT:
					spi_set_chip_delay_bct(spi1_inst, EXT_SPI_NPCS, _opts->delay_bct);
					spi_set_chip_delay_bs(spi1_inst, EXT_SPI_NPCS, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi1_inst, EXT_SPI_NPCS, _opts->bits);
					spi_set_chip_baudrate_register(spi1_inst, EXT_SPI_NPCS, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi1_inst)));
					spi_enable_chip_active_mode(spi1_inst, EXT_SPI_NPCS, _opts->keep_actv);
					spi_set_chip_mode(spi1_inst, EXT_SPI_NPCS, _opts->spi_mode);
				break;

				case serSPIUARTCTRL:
					spi_set_chip_delay_bct(spi1_inst, UART_SPI_NPCS, _opts->delay_bct);
					spi_set_chip_delay_bs(spi1_inst, UART_SPI_NPCS, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi1_inst, UART_SPI_NPCS, _opts->bits);
					spi_set_chip_baudrate_register(spi1_inst, UART_SPI_NPCS, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi1_inst)));
					spi_enable_chip_active_mode(spi1_inst, UART_SPI_NPCS, _opts->keep_actv);
					spi_set_chip_mode(spi1_inst, UART_SPI_NPCS, _opts->spi_mode);
				break;

				case serSPIROM:
					spi_set_chip_delay_bct(spi1_inst, DATAFLASH_SPI_NPCS, _opts->delay_bct);
					spi_set_chip_delay_bs(spi1_inst, DATAFLASH_SPI_NPCS, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi1_inst, DATAFLASH_SPI_NPCS, _opts->bits);
					spi_set_chip_baudrate_register(spi1_inst, DATAFLASH_SPI_NPCS, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi1_inst)));
					spi_enable_chip_active_mode(spi1_inst, DATAFLASH_SPI_NPCS, _opts->keep_actv);
					spi_set_chip_mode(spi1_inst, DATAFLASH_SPI_NPCS, _opts->spi_mode);
				break;

				case serSPISDMMC:
					spi_set_chip_delay_bct(spi1_inst, SD_MMC_SPI_NPCS, _opts->delay_bct);
					spi_set_chip_delay_bs(spi1_inst, SD_MMC_SPI_NPCS, _opts->delay_bs);
					spi_set_chip_bits_per_transfer(spi1_inst, SD_MMC_SPI_NPCS, _opts->bits);
					spi_set_chip_baudrate_register(spi1_inst, SD_MMC_SPI_NPCS, getBaudDiv(_opts->baudrate, sysclk_get_peripheral_bus_hz(spi1_inst)));
					spi_enable_chip_active_mode(spi1_inst, SD_MMC_SPI_NPCS, _opts->keep_actv);
					spi_set_chip_mode(spi1_inst, SD_MMC_SPI_NPCS, _opts->spi_mode);
				break;

				default: break;
			}

			if(!xSPI1Semaphore) {
				/* Create the SPI semaphore. */
				vSemaphoreCreateBinary(xSPI1Semaphore);
				if(xSPI1Semaphore == NULL) {
					while(1);
				}
			}

			if(!spi_is_enabled(spi1_inst)) spi_enable(spi1_inst);

			_ret_status = SPI_STATUS_OK;
		break;
		
		default: break;
	}
	
	return _ret_status;
}

/** \brief Select chip device to be active.
 *
 * \param _spi_desc		Pointer to peripheral descriptor.
 *
 * \return SPI Status
 *
 */
static spi_status_code_t spi_chip_select(spi_peripheral_descriptors_t *_spi_desc)
{
	uint8_t _timeout;
	spi_status_code_t _status = SPI_ERR_TIMEOUT;

	switch(_spi_desc->path_index) {
		case serSPILCD:
		case serSPIKEY_W:
		case serSPIKEY_R:
			/* Wait until peripheral is free. */
			_timeout = 50;
			while((pdFALSE == xSemaphoreTake(xSPI0Semaphore, 20))) {
				_timeout--;
				if(_timeout-- == 0) return SPI_ERR_TIMEOUT;
			}

			/* Assert all channel. No peripheral is selected. */
			spi0_inst->mr |= AVR32_SPI_MR_PCS_MASK;

			/* De-assert selected channel. */
			switch(_spi_desc->path_index)
			{
				case serSPILCD:
					spi0_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + LCD_SPI_NPCS));
				break;

				case serSPIKEY_W:
					spi0_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + KEY_SPI_NPCS_WR));
				break;

				case serSPIKEY_R:
					spi0_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + KEY_SPI_NPCS_RD));
				break;

				default: break;
			}
			_status = SPI_STATUS_OK;
		break;
		
		case serSPIEXT:
		case serSPIUARTCTRL:
		case serSPIROM:
		case serSPISDMMC:
			/* Wait until peripheral is free. */
			_timeout = 50;
			while(pdFALSE == xSemaphoreTake(xSPI1Semaphore, 20)) {
				_timeout--;
				if(_timeout-- == 0) return SPI_ERR_TIMEOUT;
			}

			/* Assert all channels. No peripheral is selected. */
			spi1_inst->mr |= AVR32_SPI_MR_PCS_MASK;

			/* De-assert selected channel. */
			switch(_spi_desc->path_index)
			{
				case serSPIEXT:
					spi1_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + EXT_SPI_NPCS));
				break;

				case serSPIUARTCTRL:
					spi1_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + UART_SPI_NPCS));
				break;

				case serSPIROM:
					spi1_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + DATAFLASH_SPI_NPCS));
				break;

				case serSPISDMMC:
					spi1_inst->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + SD_MMC_SPI_NPCS));
				break;

				default: break;
			}
			_status = SPI_STATUS_OK;
		break;
		
		default: break;
	}

	return _status;
}

/** \brief De-select chip device to be inactive.
 *
 * \param _spi_desc		Pointer to peripheral descriptor.
 *
 * \return SPI Status
 *
 */
static spi_status_code_t spi_chip_deselect(spi_peripheral_descriptors_t *_spi_desc)
{
	uint8_t _timeout;
	spi_status_code_t _status = SPI_ERR_TIMEOUT;

	switch(_spi_desc->path_index) {
		case serSPILCD:
		case serSPIKEY_W:
		case serSPIKEY_R:
			/* Wait until peripheral is free. */
			_timeout = 50;
			while((pdFALSE == xSemaphoreTake(xSPI0Semaphore, 20))) {
				_timeout--;
				if(_timeout-- == 0) return SPI_ERR_TIMEOUT;
			}

			/* Assert all channels. No peripheral is selected. */
			spi0_inst->mr |= AVR32_SPI_MR_PCS_MASK;

			/* Last transfer, so de-assert the current NPCS if CSAAT is set. */
			spi0_inst->cr = AVR32_SPI_CR_LASTXFER_MASK;

			/* Free the peripheral. */
			xSemaphoreGive(xSPI0Semaphore);
			_status = SPI_STATUS_OK;
		break;

		case serSPIEXT:
		case serSPIUARTCTRL:
		case serSPIROM:
		case serSPISDMMC:
			/* Wait until peripheral is free. */
			_timeout = 50;
			while((pdFALSE == xSemaphoreTake(xSPI1Semaphore, 20))) {
				_timeout--;
				if(_timeout-- == 0) return SPI_ERR_TIMEOUT;
			}

			/* Assert all channels. No peripheral is selected. */
			spi1_inst->mr |= AVR32_SPI_MR_PCS_MASK;

			/* Last transfer, so de-assert the current NPCS if CSAAT is set. */
			spi1_inst->cr = AVR32_SPI_CR_LASTXFER_MASK;

			/* Free the peripheral. */
			xSemaphoreGive(xSPI1Semaphore);
			_status = SPI_STATUS_OK;
		break;

		default: break;
	}

	return _status;
}

//----------------------------------------- Public function ----------------------------------------

spi_status_code_t spi_write_word(spi_descriptor_t _descriptor, const void *_pPacket, uint32_t _size_t)
{
	spi_status_code_t _retval;
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pPacket == NULL) return SPI_ERR_INVALID_ARG;

	if(_spi_desc->path_index == serSPIEXT) {
		if(ext_spi_is_master == true) {
			_retval = spi_master_write_start(_spi_desc, _pPacket, _size_t, true);
		}
	}
	else {
		_retval = spi_master_write_start(_spi_desc, _pPacket, _size_t, true);
	}

	return _retval;
}


spi_status_code_t spi_write_byte(spi_descriptor_t _descriptor, const void *_pPacket, uint32_t _size_t)
{
	spi_status_code_t _retval;
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pPacket == NULL) return SPI_ERR_INVALID_ARG;

	if(_spi_desc->path_index == serSPIEXT) {
		if(ext_spi_is_master == true) {
			_retval = spi_master_write_start(_spi_desc, _pPacket, _size_t, false);
		}
	}
	else {
		_retval = spi_master_write_start(_spi_desc, _pPacket, _size_t, false);
	}

	return _retval;
}


spi_status_code_t spi_read_word(spi_descriptor_t _descriptor, void *_pRxBuff, uint32_t _size_t)
{
	spi_status_code_t _retval;
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pRxBuff  == NULL) return SPI_ERR_INVALID_ARG;

	if(_spi_desc->path_index == serSPIEXT) {
		if(ext_spi_is_master == true) {
			_retval = spi_master_read_start(_spi_desc, _pRxBuff, _size_t, true);
		}
	}
	else {
		_retval = spi_master_read_start(_spi_desc, _pRxBuff, _size_t, true);
	}

	return _retval;
}

spi_status_code_t spi_read_byte(spi_descriptor_t _descriptor, void *_pRxBuff, uint32_t _size_t)
{
	spi_status_code_t _retval;
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	/* Protect unpredicted behavior if argument is NULL pointer */
	if(_pRxBuff  == NULL) return SPI_ERR_INVALID_ARG;

	if(_spi_desc->path_index == serSPIEXT) {
		if(ext_spi_is_master == true) {
			_retval = spi_master_read_start(_spi_desc, _pRxBuff, _size_t, false);
		}
	}
	else {
		_retval = spi_master_read_start(_spi_desc, _pRxBuff, _size_t, false);
	}

	return _retval;
}


spi_status_code_t spi_select_chip(spi_descriptor_t _descriptor)
{
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	return spi_chip_select(_spi_desc);
}


spi_status_code_t spi_deselect_chip(spi_descriptor_t _descriptor)
{
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	return spi_chip_deselect(_spi_desc);
}



spi_descriptor_t spi_open(SPI_path _path_name, const uint8_t _flag)
{
	uint8_t _spi_path = 0, i;

	i = 0;
	while(i < 7) {
		if(!strncmp(_path_name, spi_port_name[i], 12)) {
			_spi_path = i;
			i = 15;
		}
		i++;
	}

	/* Path name is invalid. */
	if(i < 15) return NULL;

	spi_peripheral_desc[_spi_path].path_index = _spi_path;
	spi_peripheral_desc[_spi_path].port_state = 1;

	return (void *)&spi_peripheral_desc[_spi_path];
}


spi_status_code_t spi_close(spi_descriptor_t _descriptor, const uint8_t _flag)
{
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	_spi_desc->port_state = 0;

	return SPI_STATUS_OK;
}


void spi_init(void)
{
	if(spi_already_initialized[0] == false) {
		/* Set flag. */
		spi_already_initialized[0] = true;
		
		sysclk_enable_pbc_module(SYSCLK_SPI0);
		/* Register the SPI interrupt handler to the interrupt controller and
		enable the USART interrupt. */
		INTC_register_interrupt((__int_handler)&prvSPI0_ISR, serial_SPI0_IRQ, SPI0_INT_PRIORITY);
		spi_reset(DEFAULT_SPI0);
		spi_enable_recv_fifo(DEFAULT_SPI1);
	}
	
	if(spi_already_initialized[1] == false) {
		/* Set flag. */
		spi_already_initialized[1] = true;
		
		sysclk_enable_pba_module(SYSCLK_SPI1);
		/* Register the SPI interrupt handler to the interrupt controller and
		enable the USART interrupt. */
		INTC_register_interrupt((__int_handler)&prvSPI1_ISR, serial_SPI1_IRQ, SPI1_INT_PRIORITY);
		spi_reset(DEFAULT_SPI1);
		spi_enable_recv_fifo(DEFAULT_SPI1);	
	}
}


spi_status_code_t spi_setup(spi_descriptor_t _descriptor, const spi_channel_options_t *_opt)
{
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	spi_opts[_spi_desc->path_index].baudrate = _opt->baudrate;
	spi_opts[_spi_desc->path_index].bits = _opt->bits;
	spi_opts[_spi_desc->path_index].delay_bs = _opt->delay_bs;
	spi_opts[_spi_desc->path_index].delay_bct = _opt->delay_bct;
	spi_opts[_spi_desc->path_index].keep_actv = _opt->keep_actv;
	spi_opts[_spi_desc->path_index].spi_mode = _opt->spi_mode;
	spi_opts[_spi_desc->path_index].master_setup = _opt->master_setup;

	if(spi_opts[_spi_desc->path_index].master_setup == true) {
		spi_master_chip_setup(_spi_desc, (const spi_channel_options_t *)&spi_opts[_spi_desc->path_index]);
	}

	return SPI_STATUS_OK;
}


spi_status_code_t spi_tranfer_mode(spi_descriptor_t _descriptor, spi_transfer_mode_flag_t _flag)
{
	spi_peripheral_descriptors_t *_spi_desc = _descriptor;

	if(_descriptor == NULL) return SPI_ERR_IO_ERROR;
	if(_spi_desc->path_index >= 7) return SPI_ERR_IO_ERROR;

	switch(_flag) {
		case SPI_POLLED_TX:
			_spi_desc->transfer_mode &= ~0x01;
		break;

		case SPI_POLLED_RX:
			_spi_desc->transfer_mode &= ~0x02;
		break;

		case SPI_INTERRUPT_TX:
			_spi_desc->transfer_mode |= 0x01;
		break;

		case SPI_INTERRUPT_RX:
			_spi_desc->transfer_mode |= 0x02;
		break;

		default: break;
	}

	return SPI_STATUS_OK;
}

#endif /* BOARD_1_0_USED */
